﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.datatables;
using l1cs.server.model;
using l1cs.server.serverpackets;
using l1cs.server.templates;
using l1cs.server.model.skill;
using System.Collections;

namespace l1cs.server.utils
{

    public class CalcExp
    {
        private static Logger _log = Logger.getLogger( typeof( CalcExp ).FullName );

        public static int MAX_EXP = ExpTable.getExpByLevel( 100 ) - 1;

        private CalcExp()
        {
        }

        public static void calcExp( L1PcInstance l1pcinstance, int targetid,
                ArrayList acquisitorList, List<int> hateList, int exp )
        {
            int i = 0;
            double party_level = 0;
            double dist = 0;
            int member_exp = 0;
            int member_lawful = 0;
            L1Object l1object = L1World.getInstance().findObject( targetid );
            L1NpcInstance npc = (L1NpcInstance)l1object;

            // ヘイトの合計を取得
            L1Character acquisitor;
            int hate = 0;
            int acquire_exp = 0;
            int acquire_lawful = 0;
            int party_exp = 0;
            int party_lawful = 0;
            int totalHateExp = 0;
            int totalHateLawful = 0;
            int partyHateExp = 0;
            int partyHateLawful = 0;
            int ownHateExp = 0;

            if ( acquisitorList.Count != hateList.Count )
            {
                return;
            }
            for ( i = hateList.Count - 1; i >= 0; i-- )
            {
                acquisitor = (L1Character)acquisitorList[i];
                hate = hateList[i];
                if ( acquisitor != null && !acquisitor.isDead() )
                {
                    totalHateExp += hate;
                    if ( acquisitor is L1PcInstance )
                    {
                        totalHateLawful += hate;
                    }
                }
                else
                {
                    // nullだったり死んでいたら排除
                    acquisitorList.Remove( acquisitor );
                    hateList.Remove( hate );
                }
            }
            if ( totalHateExp == 0 )
            { // 取得者がいない場合
                return;
            }

            if ( l1object != null && !( npc is L1PetInstance ) && !( npc is L1SummonInstance ) )
            {
                // int exp = npc.get_exp();
                if ( !L1World.getInstance().isProcessingContributionTotal()
                        && l1pcinstance.getHomeTownId() > 0 )
                {
                    int contribution = npc.getLevel() / 10;
                    l1pcinstance.addContribution( contribution );
                }
                int lawful = npc.getLawful();

                if ( l1pcinstance.isInParty() )
                { // パーティー中
                    // パーティーのヘイトの合計を算出
                    // パーティーメンバー以外にはそのまま配分
                    partyHateExp = 0;
                    partyHateLawful = 0;
                    for ( i = hateList.Count - 1; i >= 0; i-- )
                    {
                        acquisitor = (L1Character)acquisitorList[i];
                        hate = hateList[i];
                        if ( acquisitor is L1PcInstance )
                        {
                            L1PcInstance pc = (L1PcInstance)acquisitor;
                            if ( pc == l1pcinstance )
                            {
                                partyHateExp += hate;
                                partyHateLawful += hate;
                            }
                            else if ( l1pcinstance.getParty().isMember( pc ) )
                            {
                                partyHateExp += hate;
                                partyHateLawful += hate;
                            }
                            else
                            {
                                if ( totalHateExp > 0 )
                                {
                                    acquire_exp = ( exp * hate / totalHateExp );
                                }
                                if ( totalHateLawful > 0 )
                                {
                                    acquire_lawful = ( lawful * hate / totalHateLawful );
                                }
                                AddExp( pc, acquire_exp, acquire_lawful );
                            }
                        }
                        else if ( acquisitor is L1PetInstance )
                        {
                            L1PetInstance pet = (L1PetInstance)acquisitor;
                            L1PcInstance master = (L1PcInstance)pet.getMaster();
                            if ( master == l1pcinstance )
                            {
                                partyHateExp += hate;
                            }
                            else if ( l1pcinstance.getParty().isMember( master ) )
                            {
                                partyHateExp += hate;
                            }
                            else
                            {
                                if ( totalHateExp > 0 )
                                {
                                    acquire_exp = ( exp * hate / totalHateExp );
                                }
                                AddExpPet( pet, acquire_exp );
                            }
                        }
                        else if ( acquisitor is L1SummonInstance )
                        {
                            L1SummonInstance summon = (L1SummonInstance)acquisitor;
                            L1PcInstance master = (L1PcInstance)summon.getMaster();
                            if ( master == l1pcinstance )
                            {
                                partyHateExp += hate;
                            }
                            else if ( l1pcinstance.getParty().isMember( master ) )
                            {
                                partyHateExp += hate;
                            }
                            else
                            {
                            }
                        }
                    }
                    if ( totalHateExp > 0 )
                    {
                        party_exp = ( exp * partyHateExp / totalHateExp );
                    }
                    if ( totalHateLawful > 0 )
                    {
                        party_lawful = ( lawful * partyHateLawful / totalHateLawful );
                    }

                    // EXP、ロウフル配分

                    // プリボーナス
                    double pri_bonus = 0;
                    L1PcInstance leader = l1pcinstance.getParty().getLeader();
                    if ( leader.isCrown()
                            && ( l1pcinstance.knownsObject( leader )
                                    || l1pcinstance.Equals( leader ) ) )
                    {
                        pri_bonus = 0.059;
                    }

                    // PT経験値の計算
                    L1PcInstance[] ptMembers = l1pcinstance.getParty().getMembers();
                    double pt_bonus = 0;
                    foreach ( L1PcInstance each in ptMembers )
                    {
                        if ( l1pcinstance.knownsObject( each )
                                || l1pcinstance.Equals( each ) )
                        {
                            party_level += each.getLevel() * each.getLevel();
                        }
                        if ( l1pcinstance.knownsObject( each ) )
                        {
                            pt_bonus += 0.04;
                        }
                    }

                    party_exp = (int)( party_exp * ( 1 + pt_bonus + pri_bonus ) );

                    // 自キャラクターとそのペット・サモンのヘイトの合計を算出
                    if ( party_level > 0 )
                    {
                        dist = ( ( l1pcinstance.getLevel() * l1pcinstance.getLevel() ) / party_level );
                    }
                    member_exp = (int)( party_exp * dist );
                    member_lawful = (int)( party_lawful * dist );

                    ownHateExp = 0;
                    for ( i = hateList.Count - 1; i >= 0; i-- )
                    {
                        acquisitor = (L1Character)acquisitorList[i];
                        hate = hateList[i];
                        if ( acquisitor is L1PcInstance )
                        {
                            L1PcInstance pc = (L1PcInstance)acquisitor;
                            if ( pc == l1pcinstance )
                            {
                                ownHateExp += hate;
                            }
                        }
                        else if ( acquisitor is L1PetInstance )
                        {
                            L1PetInstance pet = (L1PetInstance)acquisitor;
                            L1PcInstance master = (L1PcInstance)pet.getMaster();
                            if ( master == l1pcinstance )
                            {
                                ownHateExp += hate;
                            }
                        }
                        else if ( acquisitor is L1SummonInstance )
                        {
                            L1SummonInstance summon = (L1SummonInstance)acquisitor;
                            L1PcInstance master = (L1PcInstance)summon.getMaster();
                            if ( master == l1pcinstance )
                            {
                                ownHateExp += hate;
                            }
                        }
                    }
                    // 自キャラクターとそのペット・サモンに分配
                    if ( ownHateExp != 0 )
                    { // 攻撃に参加していた
                        for ( i = hateList.Count - 1; i >= 0; i-- )
                        {
                            acquisitor = (L1Character)acquisitorList[i];
                            hate = hateList[i];
                            if ( acquisitor is L1PcInstance )
                            {
                                L1PcInstance pc = (L1PcInstance)acquisitor;
                                if ( pc == l1pcinstance )
                                {
                                    if ( ownHateExp > 0 )
                                    {
                                        acquire_exp = ( member_exp * hate / ownHateExp );
                                    }
                                    AddExp( pc, acquire_exp, member_lawful );
                                }
                            }
                            else if ( acquisitor is L1PetInstance )
                            {
                                L1PetInstance pet = (L1PetInstance)acquisitor;
                                L1PcInstance master = (L1PcInstance)pet
                                        .getMaster();
                                if ( master == l1pcinstance )
                                {
                                    if ( ownHateExp > 0 )
                                    {
                                        acquire_exp = ( member_exp * hate / ownHateExp );
                                    }
                                    AddExpPet( pet, acquire_exp );
                                }
                            }
                            else if ( acquisitor is L1SummonInstance )
                            {
                            }
                        }
                    }
                    else
                    { // 攻撃に参加していなかった
                        // 自キャラクターのみに分配
                        AddExp( l1pcinstance, member_exp, member_lawful );
                    }

                    // パーティーメンバーとそのペット・サモンのヘイトの合計を算出
                    for ( int cnt = 0; cnt < ptMembers.Length; cnt++ )
                    {
                        if ( l1pcinstance.knownsObject( ptMembers[cnt] ) )
                        {
                            if ( party_level > 0 )
                            {
                                dist = ( ( ptMembers[cnt].getLevel() * ptMembers[cnt]
                                        .getLevel() ) / party_level );
                            }
                            member_exp = (int)( party_exp * dist );
                            member_lawful = (int)( party_lawful * dist );

                            ownHateExp = 0;
                            for ( i = hateList.Count - 1; i >= 0; i-- )
                            {
                                acquisitor = (L1Character)acquisitorList[i];
                                hate = hateList[i];
                                if ( acquisitor is L1PcInstance )
                                {
                                    L1PcInstance pc = (L1PcInstance)acquisitor;
                                    if ( pc == ptMembers[cnt] )
                                    {
                                        ownHateExp += hate;
                                    }
                                }
                                else if ( acquisitor is L1PetInstance )
                                {
                                    L1PetInstance pet = (L1PetInstance)acquisitor;
                                    L1PcInstance master = (L1PcInstance)pet
                                            .getMaster();
                                    if ( master == ptMembers[cnt] )
                                    {
                                        ownHateExp += hate;
                                    }
                                }
                                else if ( acquisitor is L1SummonInstance )
                                {
                                    L1SummonInstance summon = (L1SummonInstance)acquisitor;
                                    L1PcInstance master = (L1PcInstance)summon
                                            .getMaster();
                                    if ( master == ptMembers[cnt] )
                                    {
                                        ownHateExp += hate;
                                    }
                                }
                            }
                            // パーティーメンバーとそのペット・サモンに分配
                            if ( ownHateExp != 0 )
                            { // 攻撃に参加していた
                                for ( i = hateList.Count - 1; i >= 0; i-- )
                                {
                                    acquisitor = (L1Character)acquisitorList[i];
                                    hate = hateList[i];
                                    if ( acquisitor is L1PcInstance )
                                    {
                                        L1PcInstance pc = (L1PcInstance)acquisitor;
                                        if ( pc == ptMembers[cnt] )
                                        {
                                            if ( ownHateExp > 0 )
                                            {
                                                acquire_exp = ( member_exp * hate / ownHateExp );
                                            }
                                            AddExp( pc, acquire_exp, member_lawful );
                                        }
                                    }
                                    else if ( acquisitor is L1PetInstance )
                                    {
                                        L1PetInstance pet = (L1PetInstance)acquisitor;
                                        L1PcInstance master = (L1PcInstance)pet
                                                .getMaster();
                                        if ( master == ptMembers[cnt] )
                                        {
                                            if ( ownHateExp > 0 )
                                            {
                                                acquire_exp = ( member_exp * hate / ownHateExp );
                                            }
                                            AddExpPet( pet, acquire_exp );
                                        }
                                    }
                                    else if ( acquisitor is L1SummonInstance )
                                    {
                                    }
                                }
                            }
                            else
                            { // 攻撃に参加していなかった
                                // パーティーメンバーのみに分配
                                AddExp( ptMembers[cnt], member_exp, member_lawful );
                            }
                        }
                    }
                }
                else
                { // パーティーを組んでいない
                    // EXP、ロウフルの分配
                    for ( i = hateList.Count - 1; i >= 0; i-- )
                    {
                        acquisitor = (L1Character)acquisitorList[i];
                        hate = hateList[i];
                        acquire_exp = ( exp * hate / totalHateExp );
                        if ( acquisitor is L1PcInstance )
                        {
                            if ( totalHateLawful > 0 )
                            {
                                acquire_lawful = ( lawful * hate / totalHateLawful );
                            }
                        }

                        if ( acquisitor is L1PcInstance )
                        {
                            L1PcInstance pc = (L1PcInstance)acquisitor;
                            AddExp( pc, acquire_exp, acquire_lawful );
                        }
                        else if ( acquisitor is L1PetInstance )
                        {
                            L1PetInstance pet = (L1PetInstance)acquisitor;
                            AddExpPet( pet, acquire_exp );
                        }
                        else if ( acquisitor is L1SummonInstance )
                        {
                        }
                    }
                }
            }
        }

        private static void AddExp( L1PcInstance pc, int exp, int lawful )
        {

            int add_lawful = (int)( lawful * Config.RATE_LA ) * -1;
            pc.addLawful( add_lawful );

            double exppenalty = ExpTable.getPenaltyRate( pc.getLevel() );
            double foodBonus = 1.0;
            if ( pc.hasSkillEffect( L1SkillId.COOKING_1_7_N )
                    || pc.hasSkillEffect( L1SkillId.COOKING_1_7_S ) )
            {
                foodBonus = 1.01;
            }
            if ( pc.hasSkillEffect( L1SkillId.COOKING_2_7_N )
                    || pc.hasSkillEffect( L1SkillId.COOKING_2_7_S ) )
            {
                foodBonus = 1.02;
            }
            if ( pc.hasSkillEffect( L1SkillId.COOKING_3_7_N )
                    || pc.hasSkillEffect( L1SkillId.COOKING_3_7_S ) )
            {
                foodBonus = 1.03;
            }
            int add_exp = (int)( exp * exppenalty * Config.RATE_XP * foodBonus );
            _log.info( string.Format( "[AddExp:{0}:{1}\n{2}", add_exp, exp, Environment.StackTrace ) );
            pc.addExp( add_exp );
        }

        private static void AddExpPet( L1PetInstance pet, int exp )
        {
            L1PcInstance pc = (L1PcInstance)pet.getMaster();

            int petNpcId = pet.getNpcTemplate().get_npcId();
            int petItemObjId = pet.getItemObjId();

            int levelBefore = pet.getLevel();
            int totalExp = (int)( exp * Config.RATE_XP + pet.getExp() );
            if ( totalExp >= ExpTable.getExpByLevel( 51 ) )
            {
                totalExp = ExpTable.getExpByLevel( 51 ) - 1;
            }
            pet.setExp( totalExp );

            pet.setLevel( ExpTable.getLevelByExp( totalExp ) );

            int expPercentage = ExpTable.getExpPercentage( pet.getLevel(), totalExp );

            int gap = pet.getLevel() - levelBefore;
            for ( int i = 1; i <= gap; i++ )
            {
                IntRange hpUpRange = pet.getPetType().getHpUpRange();
                IntRange mpUpRange = pet.getPetType().getMpUpRange();
                pet.addMaxHp( hpUpRange.randomValue() );
                pet.addMaxMp( mpUpRange.randomValue() );
            }

            pet.setExpPercent( expPercentage );
            pc.sendPackets( new S_PetPack( pet, pc ) );

            if ( gap != 0 )
            { // レベルアップしたらDBに書き込む
                L1Pet petTemplate = PetTable.getInstance().getTemplate( petItemObjId );
                if ( petTemplate == null )
                { // PetTableにない
                    _log.warning( "L1Pet == null" );
                    return;
                }
                petTemplate.set_exp( pet.getExp() );
                petTemplate.set_level( pet.getLevel() );
                petTemplate.set_hp( pet.getMaxHp() );
                petTemplate.set_mp( pet.getMaxMp() );
                PetTable.getInstance().storePet( petTemplate ); // DBに書き込み
                pc.sendPackets( new S_ServerMessage( 320, pet.getName() ) ); // \f1%0のレベルが上がりました。
            }
        }
    }
}